/* @(#)DecKanGlGlyphDictionary.java 2010
** 
** Copyright (C) 2001-2011 by Steven D. Stamps
**
**             Trademarks & Copyrights
** Flywheel Management Science(TM) and FlywheelMS(TM) are exclusive
** trademarks of Steven D. Stamps and may only be used freely for
** the purpose of identifying the unforked version of this software.
** Subsequent forks (if any) may not use these trademarks.  All other
** rights are reserved.
**
** DecKanGL (Decorated Kanban Glyph Language) and TribKn (Tribal Knowledge)
** are also exclusive trademarks of Steven D. Stamps.  These may be used
** freely within the unforked FlywheelMS application and documentation.
** All other rights are reserved.
**
** Trademark information is available at
** <http://www.flywheelms.org/trademarks>
**
** Flywheel Management Science(TM) is a copyrighted body of management
** metaphors, governance processes, and leadership techniques that is
** owned by Steven D. Stamps.  These copyrighted materials may be freely
** used, without alteration, by the community (users and developers)
** surrounding this GPL3-licensed software.  Additional copyright
** information is available at <http://www.flywheelms.org/copyrights>
**
**              GPL3 Software License
** This program is free software: you can use it, redistribute it and/or
** modify it under the terms of the GNU General Public License, version 3,
** as published by the Free Software Foundation. This program is distributed
** in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
** even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
** PURPOSE.  See the GNU General Public License for more details. You should
** have received a copy of the GNU General Public License, in a file named
** COPYING, along with this program.  If you cannot find your copy, see
** <http://www.gnu.org/licenses/gpl-3.0.html>.
*/
package com.fmscorp.deckangl.core;

import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import javax.swing.ImageIcon;

import org.apache.log4j.Logger;


/**
 * @author sstamps
 */
public abstract class DecKanGlGlyphDictionary {

	static final Logger LOGGER = Logger.getLogger(DecKanGlGlyphDictionary.class);
	
	private static final int width_ANNOTATION_LEFT_MARGIN = 4;
	private static final int width_ANNOTATION_RIGHT_MARGIN = 10;
	private static final int height_CANVAS_MARGINS = 6;
	private static final float width_ANNOTATION_CHARACTER = 5.43f;
	private static final int height_ANNOTATION_TOP_MARGIN = 12;
	private static final int height_ANNOTATION_ROW = 14;
	private static final int height_TOP_GLYPH_MARGIN = 2;
	private static final int width_GLYPH_RIGHT_MARGIN = 8;

	private static final String FILE_SEPARATOR = "/";
	protected static final boolean IS_PROTOTYPE_ICON = true;

	// DecKanGl defaults.  Override associated getters (if needed) in the implementation of DecKanGlDictionary.
	private static int margin_NOUN_STATUS_LEFT = 2;
	private static int margin_NOUN_STATUS_RIGHT = 2;
	private static int margin_NOUN_STATUS_TOP = 2;
	private static int margin_NOUN_STATUS_BOTTOM = 2;

	private static int margin_CANVAS_LEFT = 2;
	private static int margin_CANVAS_RIGHT = 2;
	private static int margin_CANVAS_TOP = 1;
	private static int margin_CANVAS_BOTTOM = 0;
	
	private static int width_LEFT_ZONE = 6;
	private static int width_RIGHT_ZONE = 6;
	
	private static int height_LEFT_CORNER_DECORATORS = 7;
	private static int height_RIGHT_CORNER_DECORATORS = 7;

	private static int overlap_BOTTOM_ZONE_DECORATORS = 1;
	private static int overlap_LEFT_ZONE_DECORATORS = 1;
	private static int overlap_RIGHT_ZONE_DECORATORS = 1;
	private static int overlap_TOP_ZONE_DECORATORS = 1;

	private static int height_LEFT_MIDDLE_DECORATORS = 7;
	private static int height_RIGHT_MIDDLE_DECORATORS = 7;
	
	private static int height_TOP_ZONE = 6;
	private static int height_BOTTOM_ZONE = 6;
	
	private static int width_TOP_DECORATORS = 8;
	private static int width_BOTTOM_DECORATORS = 8;
	
	private static Color color_CANVAS_TRANSPARENT = new Color(255, 255, 255, 255); // transparent white
	private static Color color_CANVAS_DEFAULT = new Color(213, 209, 195);
	
	private static HashMap<String, DecKanGlNounStatus> nounStatusMap;

	protected static HashMap<String, DecKanGlNoun> nounMap;
	protected static HashMap<String, DecKanGlStatus> statusMap;
	protected static HashMap<String, DecKanGlTribKn> tribKnMap;
	
	protected static DecKanGlGlyphCache decKanGlGlyphCache = new DecKanGlGlyphCache();
	
	protected static DecKanGlGlyphDictionary instance;
	
	protected DecKanGlGlyphDictionary() {
		instance = this;
		nounMap = buildNounMap();
		statusMap = buildStatusMap();
		tribKnMap = buildTribKnMap();
		disableUnusedDecoratorZones();
	}

	public static DecKanGlGlyphDictionary getInstance() {
		return instance;
	}

	public Collection<DecKanGlNoun> getNounCollection() {
		return getNounMap().values();
	}

	public HashMap<String, DecKanGlNoun> getNounMap() {
		return nounMap;
	}

	public Collection<DecKanGlStatus> getStatusCollection() {
		return getStatusMap().values();
	}

	public HashMap<String, DecKanGlStatus> getStatusMap() {
		return statusMap;
	}

	public Collection<DecKanGlTribKn> getTribKnCollection() {
		return getTribKnMap().values();
	}

	private HashMap<String, DecKanGlTribKn> getTribKnMap() {
		return tribKnMap;
	}

	public DecKanGlTribKn getTribKn(String aTribKnName) {
		return getTribKnMap().get(aTribKnName);
	}
	
	public void disableUnusedDecoratorZones() {
		if (	!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.left_zone_TOP) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.left_zone_MIDDLE) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.left_zone_BOTTOM) ) {
			width_LEFT_ZONE = 0;
			DecKanGlDecoratorCanvasZone.LEFT_ZONE.setWidth(0);
		}
		if (	!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.right_zone_TOP) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.right_zone_MIDDLE) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.right_zone_BOTTOM) ) {
			width_RIGHT_ZONE = 0;
			DecKanGlDecoratorCanvasZone.RIGHT_ZONE.setWidth(0);
		}
		if (	!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.top_zone_LEFT) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.top_zone_RIGHT) ) {
			height_TOP_ZONE = 0;
			DecKanGlDecoratorCanvasZone.TOP_ZONE.setHeight(0);
		}
		if (	!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.bottom_zone_LEFT) &&
				!tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation.bottom_zone_RIGHT) ) {
			height_BOTTOM_ZONE = 0;
			DecKanGlDecoratorCanvasZone.BOTTOM_ZONE.setHeight(0);
		}
	}

	private boolean tribKnUsesCanvasLocation(DecKanGlDecoratorCanvasLocation theCanvasLocation) {
		for (DecKanGlTribKn theDecKanGlTribKn : getInstance().getTribKnCollection()) {
			if (theDecKanGlTribKn.getLogicalCanvasLocation() == theCanvasLocation) {
				return true;
			}
		}
		return false;
	}

	// Must be over-ridden in the implementing Dictionary
	protected HashMap<String, DecKanGlTribKn> buildTribKnMap() {
		return null;
	}

	private HashMap<String, DecKanGlNounStatus> buildNounStatusMap() {
		HashMap<String, DecKanGlNounStatus> theNounWorkStatusMap =
				new HashMap<String, DecKanGlNounStatus>();
		String theNounDirectoryName;
		for (DecKanGlNoun theNoun : getInstance().getNounCollection()) {
			theNounDirectoryName = theNoun.getDirectoryName();
			for (DecKanGlStatusColor theDecKanGlStatusColor : DecKanGlStatusColor.values()) {
				theNounWorkStatusMap.put(
						buildNounStateMapKey(theNounDirectoryName, theDecKanGlStatusColor),
						new DecKanGlNounStatus(
								theNoun,
								theDecKanGlStatusColor,
								DecKanGlImageManager.directory_path_NOUN +
								FILE_SEPARATOR +
								theNounDirectoryName +
								FILE_SEPARATOR +
								theNounDirectoryName + "-" + theDecKanGlStatusColor.getName() + ".png" ) );
			}
		}
		return theNounWorkStatusMap;
	}

	public DecKanGlGlyph getDecKanGlGlyph(DecKanGlDecoratedNoun aDecKanGlDecoratedNoun) {
		String theSyntheticGlyphKey = decKanGlGlyphCache.synthesizeGlyphKey(aDecKanGlDecoratedNoun);
		DecKanGlGlyph theDecKanGlGlyph = decKanGlGlyphCache.get(theSyntheticGlyphKey);
		if (theDecKanGlGlyph == null) {
			theDecKanGlGlyph = buildDecKanGlGlyph(aDecKanGlDecoratedNoun);
			decKanGlGlyphCache.put(
					theSyntheticGlyphKey,
					theDecKanGlGlyph );
		}
		return theDecKanGlGlyph;
	}

	private DecKanGlGlyph buildDecKanGlGlyph(
			DecKanGlDecoratedNoun aDecKanGlDecoratedNoun) {
		DecKanGlNounStatus theDecKanGlNounStatus =
				getNounStatusMap().get(buildNounStatusMapKey(aDecKanGlDecoratedNoun));
		return new DecKanGlGlyph(
				theDecKanGlNounStatus,
				aDecKanGlDecoratedNoun.getDecKanGlDecoratorMap(),
				new DecKanGlCanvas(
						theDecKanGlNounStatus.getImageIcon(),
						aDecKanGlDecoratedNoun.getDecKanGlDecoratorMap(),
						getInstance() ) );
	}

	private String buildNounStatusMapKey(
			DecKanGlDecoratedNoun aDecKanGlDecoratedNoun) {
		return buildNounStateMapKey(
				aDecKanGlDecoratedNoun.getDecKanGlNoun().getDirectoryName(),
				aDecKanGlDecoratedNoun.getDecKanGlStatusColor() );
	}
	
	private String buildNounStateMapKey(String theDecoratedClassName,
			DecKanGlStatusColor theBaseImageColor) {
		return
				theDecoratedClassName +
				"-" +
				theBaseImageColor.getName();
	}

	public void putDecorator(
			HashMap<DecKanGlDecoratorCanvasLocation, DecKanGlDecorator> aDecoratorMap,
			DecKanGlDecorator aDecKanGlDecorator) {
		if (aDecKanGlDecorator != null) {
			aDecoratorMap.put(
					aDecKanGlDecorator.getTribKn().getLogicalCanvasLocation(),
					aDecKanGlDecorator);
		}
	}
	
	protected static ImageIcon buildDecKanGlImageIconZoom(
			DecKanGlGlyph aDecKanGlGlyph) {
		// TODO
		return null;
	}
	
	static ImageIcon buildDecKanGlImageIconZoomAnnotated(
			DecKanGlGlyph aDecKanGlGlyph) {
		// TODO
		return null;
	}
	
	public HashMap<String, DecKanGlNounStatus> getNounStatusMap() {
		if (nounStatusMap == null) {
			nounStatusMap = buildNounStatusMap();
		}
		return nounStatusMap;
	}

	// Must be over-ridden in the implementing Dictionary
	protected HashMap<String, DecKanGlNoun> buildNounMap() {
		return null;
	}

	public DecKanGlNoun getNoun(String aNounName) {
		return getNounMap().get(aNounName);
	}

	// Must be over-ridden in the implementing Dictionary
	protected HashMap<String, DecKanGlStatus> buildStatusMap() {
		return null;
	}

	public DecKanGlStatus getStatus(String aStatusName) {
		return getStatusMap().get(aStatusName);
	}
	
	public Color getCanvasColorTransparent() {
		return color_CANVAS_TRANSPARENT;
	}

	public Color getCanvasColorDefault() {
		return color_CANVAS_DEFAULT;
	}

	public int getWidthLeftZone() {
		return width_LEFT_ZONE;
	}

	public int getWidthRightZone() {
		return width_RIGHT_ZONE;
	}

	public int getHeightLeftCornerDecorators() {
		return height_LEFT_CORNER_DECORATORS;
	}

	public int getHeightRightCornerDecorators() {
		return height_RIGHT_CORNER_DECORATORS;
	}

	public int getHeightLeftMiddleDecorators() {
		return height_LEFT_MIDDLE_DECORATORS;
	}

	public int getHeightRightMiddleDecorators() {
		return height_RIGHT_MIDDLE_DECORATORS;
	}

	public int getHeightTopZone() {
		return height_TOP_ZONE;
	}

	public int getHeightBottomZone() {
		return height_BOTTOM_ZONE;
	}

	public int getWidthTopDecorators() {
		return width_TOP_DECORATORS;
	}

	public int getWidthBottomDecorators() {
		return width_BOTTOM_DECORATORS;
	}
	
	public int getMarginCanvasLeft() {
		return margin_CANVAS_LEFT;
	}
	
	public int getMarginCanvasRight() {
		return margin_CANVAS_RIGHT;
	}
	
	public int getMarginCanvasTop() {
		return margin_CANVAS_TOP;
	}
	
	public int getMarginCanvasBottom() {
		return margin_CANVAS_BOTTOM;
	}
	
	public int getMarginNounStatusLeft() {
		return margin_NOUN_STATUS_LEFT;
	}
	
	public int getMarginNounStatusRight() {
		return margin_NOUN_STATUS_RIGHT;
	}
	
	public int getMarginNounStatusTop() {
		return margin_NOUN_STATUS_TOP;
	}
	
	public int getMarginNounStatusBottom() {
		return margin_NOUN_STATUS_BOTTOM;
	}

	public int getOverlapBottomZoneDecorators() {
		return overlap_BOTTOM_ZONE_DECORATORS;
	}

	public int getOverlapLeftZoneDecorators() {
		return overlap_LEFT_ZONE_DECORATORS;
	}

	public int getOverlapRightZoneDecorators() {
		return overlap_RIGHT_ZONE_DECORATORS;
	}

	public int getOverlapTopZoneDecorators() {
		return overlap_TOP_ZONE_DECORATORS;
	}

	public int getHeightLeftZone() {
		return (getHeightLeftCornerDecorators() * 2) +
				getHeightLeftMiddleDecorators() -
				(getOverlapLeftZoneDecorators() * 2);
	}

	public int getHeightRightZone() {
		return (getHeightRightCornerDecorators() * 2) +
				getHeightRightMiddleDecorators() -
				(getOverlapRightZoneDecorators() * 2);
	}

	public int getWidthTopZone() {
		return (getWidthTopDecorators() * 2) -
				getOverlapTopZoneDecorators();
	}

	public int getWidthBottomZone() {
		return (getWidthBottomDecorators() * 2) -
				getOverlapBottomZoneDecorators();
	}
	
	public static boolean decoratorsUseZone(
			HashMap<DecKanGlDecoratorCanvasLocation, DecKanGlDecorator> aDecoratorMap,
			DecKanGlDecoratorCanvasZone aCanvasZone) {
		for (DecKanGlDecorator theDecKanGlDecorator : aDecoratorMap.values()) {
			if (theDecKanGlDecorator.isEnabled() &&
				theDecKanGlDecorator.getTribKn().getLogicalCanvasLocation().getCanvasZone() ==
					aCanvasZone) {
				return true;
			}
		}
		return false;
	}

	public static ImageIcon zoomGlyph(ImageIcon anOriginalImageIcon){
		return new ImageIcon(anOriginalImageIcon.getImage().getScaledInstance(
				anOriginalImageIcon.getIconWidth() * 2,
				anOriginalImageIcon.getIconHeight() * 2,
				BufferedImage.TYPE_INT_ARGB ) );
	}	

	public static ImageIcon zoomGlyphWithAnnotation(DecKanGlGlyph aDecKanGlGlyph){
		ImageIcon theZoomedImageIcon = zoomGlyph(aDecKanGlGlyph.getImageIcon());
		ArrayList<String> theLeftAnnotationList =  aDecKanGlGlyph.getLeftAnnotations();
		ArrayList<String> theRightAnnotationList =  aDecKanGlGlyph.getRightAnnotations();
		int theLeftAnnotationWidth = (int)(getMaximumAnnotationLength(theLeftAnnotationList) * width_ANNOTATION_CHARACTER);
		int theRightAnnotationWidth = (int)(getMaximumAnnotationLength(theRightAnnotationList) * width_ANNOTATION_CHARACTER);
		BufferedImage theBufferedImageCanvas = new BufferedImage(
				width_ANNOTATION_LEFT_MARGIN + theLeftAnnotationWidth + theZoomedImageIcon.getIconWidth() + theRightAnnotationWidth + width_ANNOTATION_RIGHT_MARGIN,
				theZoomedImageIcon.getIconHeight() + height_CANVAS_MARGINS,
				BufferedImage.TYPE_INT_ARGB);
		Graphics theGraphicsContext = theBufferedImageCanvas.getGraphics();
		theGraphicsContext.setColor(aDecKanGlGlyph.getCanvas().getColor());
		theGraphicsContext.fillRect(0, 0, theBufferedImageCanvas.getWidth(), theBufferedImageCanvas.getHeight());
		Graphics2D theGraphics2dCanvas = theBufferedImageCanvas.createGraphics();
		theGraphics2dCanvas.setComposite(AlphaComposite.SrcOver);
		theGraphics2dCanvas.drawImage(
				theZoomedImageIcon.getImage(),
				theLeftAnnotationWidth + 2,
				height_TOP_GLYPH_MARGIN,
				null);  // nobody needs to be notified while we are drawing
		int theRow = 0;
		theGraphicsContext.setFont( new Font("Sansserif",Font.PLAIN,10));
		theGraphicsContext.setColor(Color.BLACK);
		for (String theAnnotation : theLeftAnnotationList) {
			theGraphicsContext.drawString(
					theAnnotation,
					width_ANNOTATION_LEFT_MARGIN,
					(theRow * height_ANNOTATION_ROW) + height_ANNOTATION_TOP_MARGIN );
			++theRow;
		}
		theRow = 0;
		for (String theAnnotation : theRightAnnotationList) {
			theGraphicsContext.drawString(
					theAnnotation,
					theLeftAnnotationWidth + theZoomedImageIcon.getIconWidth() + width_GLYPH_RIGHT_MARGIN,
					(theRow * height_ANNOTATION_ROW) + height_ANNOTATION_TOP_MARGIN );
			++theRow;
		}
		return new ImageIcon(theBufferedImageCanvas);
	}
	
	private static int getMaximumAnnotationLength(ArrayList<String> anAnnotationList) {
		int theMaximumLength = 0;
		for (String theAnnotation : anAnnotationList) {
			theMaximumLength = theAnnotation.length() > theMaximumLength ? theAnnotation.length() : theMaximumLength;
		}
		return theMaximumLength;
	}

}